﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using System.Reflection;

namespace HexTileBoard
{
    public static class BoardConstructor
    {
        public static Random random = new Random();
        //public static int objectsDrawn = 0;
        //public static int branchesVisited = 0;

        // public static List<T>
        public static Array GetValues<T>()    
        {      
            Type currentEnum = typeof(T);      
            List<T> resultSet = new List<T>();      
            if (currentEnum.IsEnum)      
            {        
                FieldInfo[] fields = currentEnum.GetFields(BindingFlags.Static | BindingFlags.Public);
                foreach (FieldInfo field in fields) 
                    resultSet.Add((T)field.GetValue(null));
            }     
            
            return resultSet.ToArray(); 
        }

        /// <summary>
        /// Construct a hex shaped board.
        /// </summary>
        /// <param name="_tiles">The list of tiles to create.</param>
        /// <param name="_dimension">The dimensions of the board hex - the length of any given side.</param>
        /// <remarks>The board generates ranks and files in this fashion, where A-E are files and numerics are ranks:
        /// A3 B4 C5 D4 E3
        /// A2 B3 C4 D3 E2
        /// A1 B2 C3 D2 E1
        ///    B1 C2 D1
        ///       C1
        ///       
        /// tile neighbors:
        ///   0
        /// 3   2
        ///   T
        /// 5   4
        ///   1
        /// </remarks>
        public static void ConstructGameBoard(ref TileSimple[] _tiles, ref Dictionary<HexTileResource, List<TileSimple>> _resourcePositions, ref Billboard _billboard, short _dimension, int _seed)
        {
            if (_seed != -1)
            {
                random = new Random(_seed);
            }

            // Initialize the tile array, discovering the proper array size
            int index = 0;
            int numTiles = 1;
            for (int i = _dimension - 1; i > 0; i--)
                numTiles += (6 * i);
            _tiles = new TileSimple[numTiles];

            // Tiles are constructed in increasing rank, then file, such as A1, A2, ... A6, B1, B2...
            // The assumption is that the board is square in this case. C# rounsd integer division to the floor, so add one.
            for (int i = 0; i < _dimension; i++)
            {
                for (int j = 0; j < _dimension + i; j++)
                {
                    _tiles[index] = new TileSimple(index, i, j, Vector3.Zero, Vector3.Zero, 1.0f);
                    index++;
                }
            }
            // Construct the last 5 files (G - L inclusive) with all their ranks (decreasing from 10 max)...
            for (int i = _dimension; i < (_dimension * 2) - 1; i++)
            {
                for (int j = 0; j < (_dimension * 2) - 1 + (_dimension - 1) - i; j++)
                {
                    _tiles[index] = new TileSimple(index, i, j, Vector3.Zero, Vector3.Zero, 1.0f);
                    index++;
                }
            }

            // Construct the neighbors
            for (index = 0; index < _tiles.Length; index++)
            {
                // Contruct [0] and [1] neighbors
                if (index != _tiles.Length - 1 && _tiles[index].file == _tiles[index + 1].file)
                    _tiles[index].Neighbors[0] = _tiles[index + 1];
                if (index != 0 && _tiles[index].file == _tiles[index - 1].file)
                    _tiles[index].Neighbors[1] = _tiles[index - 1];

                // Construct the rest
                if (_tiles[index].file < _dimension - 1)                     // 0 to _dimension - 1
                {
                    _tiles[index].Neighbors[2] = _tiles[index + _dimension + 1 + _tiles[index].file];
                    _tiles[index].Neighbors[4] = _tiles[index + _dimension + _tiles[index].file];
                    if (_tiles[index].file != 0)
                    {
                        if (_tiles[index].file == _tiles[index + 1].file)
                            _tiles[index].Neighbors[3] = _tiles[index - _dimension + 1 - _tiles[index].file];
                        if (_tiles[index].rank != 0)
                            _tiles[index].Neighbors[5] = _tiles[index - _dimension - _tiles[index].file];
                    }
                }
                else if (_tiles[index].file == _dimension - 1)               // Middle column
                {
                    if (_tiles[index].rank != ((_dimension * 2) - 2))
                    {
                        _tiles[index].Neighbors[2] = _tiles[index + ((_dimension * 2) - 1)];
                        _tiles[index].Neighbors[3] = _tiles[index - ((_dimension * 2) - 1) + 1];
                    }
                    if (_tiles[index].rank != 0)
                    {
                        _tiles[index].Neighbors[4] = _tiles[index + ((_dimension * 2) - 1) - 1];
                        _tiles[index].Neighbors[5] = _tiles[index - ((_dimension * 2) - 1)];
                    }
                }
                else                                                // _dimension to _dimension * 2 - 1
                {
                    _tiles[index].Neighbors[3] = _tiles[index - ((_dimension * 2) - 1) + (_tiles[index].file - _dimension) + 1];
                    _tiles[index].Neighbors[5] = _tiles[index - ((_dimension * 2) - 1) + (_tiles[index].file - _dimension)];
                    if (_tiles[index].file != ((_dimension * 2) - 2))
                    {
                        if (_tiles[index].file == _tiles[index + 1].file)
                            _tiles[index].Neighbors[2] = _tiles[index + ((_dimension * 2) - 1) - _tiles[index].file + _dimension - 1];
                        if (_tiles[index].rank != 0)
                            _tiles[index].Neighbors[4] = _tiles[index + ((_dimension * 2) - 1) - _tiles[index].file + _dimension - 2];
                    }
                }
            }

            // Place the models in 3D space
            PlaceModelPositions(ref _tiles, _dimension);

            // Generate the terrain specific to this implementation
            CreateTerrain(ref _tiles, ref _resourcePositions, ref _billboard, _dimension);
        }

        private static void PlaceModelPositions(ref TileSimple[] tiles, short _dimension)
        {
            // Now, assign the models position in 3D space
            // Files move in increasing Z space, Ranks in increasing/decreasing X space.
            // Example: using tileDimZ: 46 and tileDimX: 78
            // X increases along the files from A to I/L
            // Z decreases along the ranks from 1 to 10/11
            // Example, {X, Z}: _dimension == 6: A1{0, 0} A6{0, -230} F1{195, 115} F11{195, -345} L1{390, 0} L6{390, -230}
            
            float XPos = 0.0f;
            float ZPos = 0.0f;
            float tileDimZ = BoardVariables.tileModelDepth;
            float tileDimX = BoardVariables.tileModelWidth;

            for (int i = 0; i < tiles.Length; i++)
            {
                // On a new file
                if (i != 0 && tiles[i].file != tiles[i - 1].file)
                {
                    XPos += tileDimX / 2;    // something to this effect, shift enough over to interlock

                    if (tiles[i].file < _dimension)
                    {
                        ZPos = +(tileDimZ / 2) * (int)tiles[i].file;
                    }
                    else
                    {
                        ZPos = +((tileDimZ / 2) * ((int)(_dimension * 2) - 2 - (int)tiles[i].file));
                    }
                }

                tiles[i].Position = new Vector3(XPos, 0, ZPos);

                ZPos -= tileDimZ;
            }
        }

        private static void CreateTerrain(ref TileSimple[] _tiles, ref Dictionary<HexTileResource, List<TileSimple>> _resourcePositions, ref Billboard _billboard, short _dimension)
        {
            PlaceMountains(ref _tiles, (int)MathHelper.Max(4, _dimension / 3));
            PlaceHills(ref _tiles, _tiles.Length / 10, _tiles.Length / 2);
            PlaceForests(ref _tiles, 8, 50);
            PlaceDesert(ref _tiles, (int)MathHelper.Max(2, _dimension / 10));
            PlaceRiver(ref _tiles, (int)MathHelper.Max(3, _dimension / 5));

            // Assign resources and randomize the model, if applicable
            // Lists to contain relevant tiles to make sure we get a minimum number of resources
            List<TileSimple> hills = new List<TileSimple>();
            List<TileSimple> mountains = new List<TileSimple>();
            List<TileSimple> trees = new List<TileSimple>();
            List<TileSimple> grass = new List<TileSimple>();

            // Get possible resource locations
            for (int i = 0; i < _tiles.Length; i++)
            {
                switch (_tiles[i].NaturalModifier)
                {
                    case HexTileNaturalModifier.None:
                        if (_tiles[i].TileBase == HexTileBase.Grass)
                            grass.Add(_tiles[i]); break;
                    case HexTileNaturalModifier.Mountain1:
                    case HexTileNaturalModifier.Mountain2:
                    case HexTileNaturalModifier.Mountain3:
                        mountains.Add(_tiles[i]); break;
                    case HexTileNaturalModifier.Hill2:
                    //case HexTileNaturalModifier.Hill2:
                    //case HexTileNaturalModifier.Hill2:
                        hills.Add(_tiles[i]); break;
                    case HexTileNaturalModifier.Trees1:
                    //case HexTileNaturalModifier.Trees1:
                        trees.Add(_tiles[i]); break;
                }
            }
            
            // Wheat
            for (int i = 0; i < (int)(_dimension * 2.0f); i++)
            {
                int wheatLocation = random.Next(0, grass.Count);
                _tiles[grass[wheatLocation].IndexInBoard].ResourceModifier = HexTileResource.Wheat;
                _tiles[grass[wheatLocation].IndexInBoard].NaturalModifier = HexTileNaturalModifier.Wheat;

                // add for billboarding
                _resourcePositions[HexTileResource.Wheat].Add(_tiles[grass[wheatLocation].IndexInBoard]);
            }
            // wood - make sure trees exist first
            if (trees.Count < (int)_dimension * 2)
            {
                for (int i = 0; i < (int)_dimension; i++)
                {
                    int r = random.Next(0, _tiles.Length);
                    _tiles[r].NaturalModifier = HexTileNaturalModifier.Trees1;
                }
            }
            for (int i = 0; i < trees.Count; i++)
            {
                _tiles[trees[i].IndexInBoard].ResourceModifier = HexTileResource.Wood;
                // add for billboarding
                _resourcePositions[HexTileResource.Wood].Add(_tiles[trees[i].IndexInBoard]);

                int r = random.Next(0, 2);
                if (r == 0)
                    _tiles[trees[i].IndexInBoard].NaturalModifier = HexTileNaturalModifier.Trees1;
                else
                    _tiles[trees[i].IndexInBoard].NaturalModifier = HexTileNaturalModifier.Trees1;
            }
            // clay
            for (int i = 0; i < (hills.Count / 4); i++)
            {
                int clayLocation = random.Next(0, hills.Count);
                _tiles[hills[clayLocation].IndexInBoard].ResourceModifier = HexTileResource.Clay;

                // add for billboarding
                _resourcePositions[HexTileResource.Clay].Add(_tiles[hills[clayLocation].IndexInBoard]);
            }
            for (int i = 0; i < hills.Count; i++)
            {
                int r = random.Next(0, 3);
                if (r == 0)
                    _tiles[hills[i].IndexInBoard].NaturalModifier = HexTileNaturalModifier.Hill2;
                else if (r == 1)
                    _tiles[hills[i].IndexInBoard].NaturalModifier = HexTileNaturalModifier.Hill2;
                else
                    _tiles[hills[i].IndexInBoard].NaturalModifier = HexTileNaturalModifier.Hill2;
            }
            // stone
            for (int i = 0; i < (mountains.Count / 2); i++)
            {
                int stoneLocation = random.Next(0, mountains.Count);
                _tiles[mountains[stoneLocation].IndexInBoard].ResourceModifier = HexTileResource.Stone;

                // add for billboarding
                _resourcePositions[HexTileResource.Stone].Add(_tiles[mountains[stoneLocation].IndexInBoard]);
            }
            // iron - must be less than stone, guarantee some stone exists. Should be very rare
            int ironCount = Math.Max(2, mountains.Count / 20);
            for (int i = 0; i < ironCount; i++)
            {
                int ironLocation = random.Next(0, mountains.Count);

                if (_tiles[mountains[ironLocation].IndexInBoard].ResourceModifier == HexTileResource.Stone)
                {
                    _resourcePositions[HexTileResource.Stone].Remove(_tiles[mountains[ironLocation].IndexInBoard]);
                }

                // add for billboarding
                _tiles[mountains[ironLocation].IndexInBoard].ResourceModifier = HexTileResource.Iron;
                _resourcePositions[HexTileResource.Iron].Add(_tiles[mountains[ironLocation].IndexInBoard]);
            }

            // randomize the mountain model
            for (int i = 0; i < mountains.Count; i++)
            {
                int r = random.Next(0, 3);
                if (r == 0)
                    _tiles[mountains[i].IndexInBoard].NaturalModifier = HexTileNaturalModifier.Mountain1;
                else if (r == 1)
                    _tiles[mountains[i].IndexInBoard].NaturalModifier = HexTileNaturalModifier.Mountain2;
                else
                    _tiles[mountains[i].IndexInBoard].NaturalModifier = HexTileNaturalModifier.Mountain3;
            }

            for (int i = 0; i < _tiles.Length; i++)
            {
                _tiles[i].PreviousNaturalModifier = _tiles[i].NaturalModifier;
            }

            // Add resource positions to billboarding
            _billboard.AddBillboardItem(HexTileResource.Clay, _resourcePositions[HexTileResource.Clay]);
            _billboard.AddBillboardItem(HexTileResource.Iron, _resourcePositions[HexTileResource.Iron]);
            _billboard.AddBillboardItem(HexTileResource.Stone, _resourcePositions[HexTileResource.Stone]);
            _billboard.AddBillboardItem(HexTileResource.Wheat, _resourcePositions[HexTileResource.Wheat]);
            _billboard.AddBillboardItem(HexTileResource.Wood, _resourcePositions[HexTileResource.Wood]);
        }

        private static void PlaceMountains(ref TileSimple[] _tiles, int _numRanges)
        {
            // create mountains
            for (int i = 0; i < _numRanges; i++)
            {
                // which direction the range will run in
                // for each extension, can be in one of 6 directions from start tile:
                // [0, 2], [2, 4], [4, 1], [1, 5], [5, 3], [3,0]
                // mountains can extend in one or both of the available tiles
                int rangeDirection = random.Next(0, 6);

                // which tile to start on
                int startTile = random.Next(0, _tiles.Length);
                bool onBoard = true;

                // build in the direction
                while (onBoard)
                {
                    switch (rangeDirection)
                    {
                        case (0):
                            if (_tiles[startTile].Neighbors[0] == null || _tiles[startTile].Neighbors[2] == null)
                                onBoard = false;
                            else
                            {
                                if (random.Next(1, 3) == 2)	// both _tiles
                                {
                                    _tiles[startTile].Neighbors[0].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    _tiles[startTile].Neighbors[2].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    if (random.Next(1, 3) == 1)
                                        startTile = _tiles[startTile].Neighbors[0].IndexInBoard;
                                    else
                                        startTile = _tiles[startTile].Neighbors[2].IndexInBoard;
                                }
                                else
                                {
                                    if (random.Next(1, 3) == 1)
                                    {
                                        _tiles[startTile].Neighbors[0].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[0].IndexInBoard;
                                    }
                                    else
                                    {
                                        _tiles[startTile].Neighbors[2].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[2].IndexInBoard;
                                    }
                                }
                            }
                            break;
                        case (1):
                            if (_tiles[startTile].Neighbors[2] == null || _tiles[startTile].Neighbors[4] == null)
                                onBoard = false;
                            else
                            {
                                if (random.Next(1, 3) == 2)	// both _tiles
                                {
                                    _tiles[startTile].Neighbors[2].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    _tiles[startTile].Neighbors[4].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    if (random.Next(1, 3) == 1)
                                        startTile = _tiles[startTile].Neighbors[2].IndexInBoard;
                                    else
                                        startTile = _tiles[startTile].Neighbors[4].IndexInBoard;
                                }
                                else
                                {
                                    if (random.Next(1, 3) == 1)
                                    {
                                        _tiles[startTile].Neighbors[2].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[2].IndexInBoard;
                                    }
                                    else
                                    {
                                        _tiles[startTile].Neighbors[4].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[4].IndexInBoard;
                                    }
                                }
                            }
                            break;
                        case (2):
                            if (_tiles[startTile].Neighbors[4] == null || _tiles[startTile].Neighbors[1] == null)
                                onBoard = false;
                            else
                            {
                                if (random.Next(1, 3) == 2)	// both _tiles
                                {
                                    _tiles[startTile].Neighbors[4].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    _tiles[startTile].Neighbors[1].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    if (random.Next(1, 3) == 1)
                                        startTile = _tiles[startTile].Neighbors[4].IndexInBoard;
                                    else
                                        startTile = _tiles[startTile].Neighbors[1].IndexInBoard;
                                }
                                else
                                {
                                    if (random.Next(1, 3) == 1)
                                    {
                                        _tiles[startTile].Neighbors[4].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[4].IndexInBoard;
                                    }
                                    else
                                    {
                                        _tiles[startTile].Neighbors[1].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[1].IndexInBoard;
                                    }
                                }
                            }
                            break;
                        case (3):
                            if (_tiles[startTile].Neighbors[1] == null || _tiles[startTile].Neighbors[5] == null)
                                onBoard = false;
                            else
                            {
                                if (random.Next(1, 3) == 2)	// both _tiles
                                {
                                    _tiles[startTile].Neighbors[1].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    _tiles[startTile].Neighbors[5].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    if (random.Next(1, 3) == 1)
                                        startTile = _tiles[startTile].Neighbors[1].IndexInBoard;
                                    else
                                        startTile = _tiles[startTile].Neighbors[5].IndexInBoard;
                                }
                                else
                                {
                                    if (random.Next(1, 3) == 1)
                                    {
                                        _tiles[startTile].Neighbors[1].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[1].IndexInBoard;
                                    }
                                    else
                                    {
                                        _tiles[startTile].Neighbors[5].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[5].IndexInBoard;
                                    }
                                }
                            }
                            break;
                        case (4):
                            if (_tiles[startTile].Neighbors[5] == null || _tiles[startTile].Neighbors[3] == null)
                                onBoard = false;
                            else
                            {
                                if (random.Next(1, 3) == 2)	// both _tiles
                                {
                                    _tiles[startTile].Neighbors[5].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    _tiles[startTile].Neighbors[3].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    if (random.Next(1, 3) == 1)
                                        startTile = _tiles[startTile].Neighbors[5].IndexInBoard;
                                    else
                                        startTile = _tiles[startTile].Neighbors[3].IndexInBoard;
                                }
                                else
                                {
                                    if (random.Next(1, 3) == 1)
                                    {
                                        _tiles[startTile].Neighbors[5].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[5].IndexInBoard;
                                    }
                                    else
                                    {
                                        _tiles[startTile].Neighbors[3].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[3].IndexInBoard;
                                    }
                                }
                            }
                            break;
                        case (5):
                            if (_tiles[startTile].Neighbors[3] == null || _tiles[startTile].Neighbors[0] == null)
                                onBoard = false;
                            else
                            {
                                if (random.Next(1, 3) == 2)	// both _tiles
                                {
                                    _tiles[startTile].Neighbors[3].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    _tiles[startTile].Neighbors[0].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                    if (random.Next(1, 3) == 1)
                                        startTile = _tiles[startTile].Neighbors[3].IndexInBoard;
                                    else
                                        startTile = _tiles[startTile].Neighbors[0].IndexInBoard;
                                }
                                else
                                {
                                    if (random.Next(1, 3) == 1)
                                    {
                                        _tiles[startTile].Neighbors[3].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[3].IndexInBoard;
                                    }
                                    else
                                    {
                                        _tiles[startTile].Neighbors[0].NaturalModifier = HexTileNaturalModifier.Mountain1;
                                        startTile = _tiles[startTile].Neighbors[0].IndexInBoard;
                                    }
                                }
                            }
                            break;
                    }
                }
            }
        }

        private static void PlaceHills(ref TileSimple[] _tiles, int lowerBounds, int upperBounds)
        {
            // Certain number of hills to generate
            int hills = random.Next(lowerBounds, upperBounds);

            // set up to the "hills" count as hills (can be less, random is not exclusive of existing hills)
            for (int i = 0; i < hills; i++)
            {
                _tiles[random.Next(0, _tiles.Length)].NaturalModifier = HexTileNaturalModifier.Hill2;
            }
        }

        /// <summary>
        /// Places trees on a map, with a basis probability and a clustering probability.
        /// </summary>
        /// <param name="_tiles"></param>
        /// <param name="_baseProbability"></param>
        /// <param name="_extendProbability"></param>
        private static void PlaceForests(ref TileSimple[] _tiles, int _baseProbability, int _extendProbability)
        {
            for (int i = 0; i < _tiles.Length; i++)
            {
                if (_tiles[i].NaturalModifier == HexTileNaturalModifier.None)
                {
                    if (random.Next(0, 101) <= _baseProbability)
                    {
                        _tiles[i].NaturalModifier = HexTileNaturalModifier.Trees1;
                    }
                    else if (((_tiles[i].Neighbors[0] != null && _tiles[i].Neighbors[0].NaturalModifier == HexTileNaturalModifier.Trees1) ||
                        (_tiles[i].Neighbors[1] != null && _tiles[i].Neighbors[1].NaturalModifier == HexTileNaturalModifier.Trees1) ||
                        (_tiles[i].Neighbors[2] != null && _tiles[i].Neighbors[2].NaturalModifier == HexTileNaturalModifier.Trees1) ||
                        (_tiles[i].Neighbors[3] != null && _tiles[i].Neighbors[3].NaturalModifier == HexTileNaturalModifier.Trees1) ||
                        (_tiles[i].Neighbors[4] != null && _tiles[i].Neighbors[4].NaturalModifier == HexTileNaturalModifier.Trees1) ||
                        (_tiles[i].Neighbors[5] != null && _tiles[i].Neighbors[5].NaturalModifier == HexTileNaturalModifier.Trees1)) &&
                        random.Next(0, 101) <= _extendProbability)
                    {
                        _tiles[i].NaturalModifier = HexTileNaturalModifier.Trees1;
                    }
                }
            }
        }

        private static void PlaceDesert(ref TileSimple[] _tiles, int _numDeserts)
        {
            // construct a desert
            for (int x = 0; x < _numDeserts; x++)
            {
                int desertTile = random.Next(0, _tiles.Length);
                _tiles[desertTile].TileBase = HexTileBase.Sand;
                _tiles[desertTile].NaturalModifier = HexTileNaturalModifier.None;

                for (int i = 0; i < 6; i++)
                {
                    if (_tiles[desertTile].Neighbors[i] != null)
                    {
                        _tiles[desertTile].Neighbors[i].TileBase = HexTileBase.Sand;
                        _tiles[desertTile].Neighbors[i].NaturalModifier = HexTileNaturalModifier.None;
                    }
                }
            }
        }

        private static void PlaceRiver(ref TileSimple[] _tiles, int _numRivers)
        {
            // create river
            // which direction the river will run in
            // for each extension, can be in one of 6 directions from start tile:
            // [0, 2, 4], [2, 4, 1], [4, 1, 5], [1, 5, 3], [5, 3, 0], [3, 0, 2]
            // rivers can extend in one direction only
            for (int i = 0; i < _numRivers; i++)
            {
                int rangeDirection = random.Next(0, 6);

                // which tile to start on
                int riverTile = random.Next(0, _tiles.Length);
                bool onBoard = true;
                int hold;

                // build in the direction, stopping when off the board or hitting another river
                // James: removed random(0, 3) to just select 2 possible paths, makes straighter rivers, changed to (0, 3) to make curvy rivers
                while (onBoard)
                {
                    switch (rangeDirection)
                    {
                        case (0):
                            if (_tiles[riverTile].Neighbors[0] == null || _tiles[riverTile].Neighbors[2] == null || _tiles[riverTile].Neighbors[4] == null ||
                                _tiles[riverTile].Neighbors[0].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[2].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[4].TileBase == HexTileBase.Water)
                                onBoard = false;
                            else
                            {
                                hold = random.Next(0, 2);
                                if (hold == 0)
                                {
                                    _tiles[riverTile].Neighbors[0].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[0].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[0].IndexInBoard;
                                }
                                else if (hold == 1)
                                {
                                    _tiles[riverTile].Neighbors[2].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[2].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[2].IndexInBoard;
                                }
                                else
                                {
                                    _tiles[riverTile].Neighbors[4].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[4].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[4].IndexInBoard;
                                }
                            }
                            break;
                        case (1):
                            if (_tiles[riverTile].Neighbors[2] == null || _tiles[riverTile].Neighbors[4] == null || _tiles[riverTile].Neighbors[1] == null ||
                                _tiles[riverTile].Neighbors[2].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[4].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[1].TileBase == HexTileBase.Water)
                                onBoard = false;
                            else
                            {
                                hold = random.Next(0, 2);
                                if (hold == 0)
                                {
                                    _tiles[riverTile].Neighbors[2].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[2].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[2].IndexInBoard;
                                }
                                else if (hold == 1)
                                {
                                    _tiles[riverTile].Neighbors[4].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[4].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[4].IndexInBoard;
                                }
                                else
                                {
                                    _tiles[riverTile].Neighbors[1].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[1].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[1].IndexInBoard;
                                }
                            }
                            break;
                        case (2):
                            if (_tiles[riverTile].Neighbors[4] == null || _tiles[riverTile].Neighbors[1] == null || _tiles[riverTile].Neighbors[5] == null ||
                                _tiles[riverTile].Neighbors[4].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[1].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[5].TileBase == HexTileBase.Water)
                                onBoard = false;
                            else
                            {
                                hold = random.Next(0, 2);
                                if (hold == 0)
                                {
                                    _tiles[riverTile].Neighbors[4].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[4].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[4].IndexInBoard;
                                }
                                else if (hold == 1)
                                {
                                    _tiles[riverTile].Neighbors[1].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[1].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[1].IndexInBoard;
                                }
                                else
                                {
                                    _tiles[riverTile].Neighbors[5].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[5].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[5].IndexInBoard;
                                }
                            }
                            break;
                        case (3):
                            if (_tiles[riverTile].Neighbors[1] == null || _tiles[riverTile].Neighbors[5] == null || _tiles[riverTile].Neighbors[3] == null ||
                                _tiles[riverTile].Neighbors[1].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[5].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[3].TileBase == HexTileBase.Water)
                                onBoard = false;
                            else
                            {
                                hold = random.Next(0, 2);
                                if (hold == 0)
                                {
                                    _tiles[riverTile].Neighbors[1].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[1].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[1].IndexInBoard;
                                }
                                else if (hold == 1)
                                {
                                    _tiles[riverTile].Neighbors[5].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[5].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[5].IndexInBoard;
                                }
                                else
                                {
                                    _tiles[riverTile].Neighbors[3].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[3].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[3].IndexInBoard;
                                }
                            }
                            break;
                        case (4):
                            if (_tiles[riverTile].Neighbors[5] == null || _tiles[riverTile].Neighbors[3] == null || _tiles[riverTile].Neighbors[0] == null ||
                                _tiles[riverTile].Neighbors[5].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[3].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[0].TileBase == HexTileBase.Water)
                                onBoard = false;
                            else
                            {
                                hold = random.Next(0, 2);
                                if (hold == 0)
                                {
                                    _tiles[riverTile].Neighbors[5].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[5].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[5].IndexInBoard;
                                }
                                else if (hold == 1)
                                {
                                    _tiles[riverTile].Neighbors[3].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[3].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[3].IndexInBoard;
                                }
                                else
                                {
                                    _tiles[riverTile].Neighbors[0].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[0].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[0].IndexInBoard;
                                }
                            }
                            break;
                        case (5):
                            if (_tiles[riverTile].Neighbors[3] == null || _tiles[riverTile].Neighbors[0] == null || _tiles[riverTile].Neighbors[2] == null ||
                                _tiles[riverTile].Neighbors[3].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[0].TileBase == HexTileBase.Water || _tiles[riverTile].Neighbors[2].TileBase == HexTileBase.Water)
                                onBoard = false;
                            else
                            {
                                hold = random.Next(0, 2);
                                if (hold == 0)
                                {
                                    _tiles[riverTile].Neighbors[3].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[3].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[3].IndexInBoard;
                                }
                                else if (hold == 1)
                                {
                                    _tiles[riverTile].Neighbors[0].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[0].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[0].IndexInBoard;
                                }
                                else
                                {
                                    _tiles[riverTile].Neighbors[2].TileBase = HexTileBase.Water;
                                    _tiles[riverTile].Neighbors[2].NaturalModifier = HexTileNaturalModifier.None;
                                    riverTile = _tiles[riverTile].Neighbors[2].IndexInBoard;
                                }
                            }
                            break;
                    }
                }
            }
        }

        public static void ConstructQuadTreeAndInstancing(ContentManager _content, GraphicsDevice _graphics, TileSimple[] _tiles, ref Quadtree _quadtree, short _dim)
        {
            // Initialize the dictionaries for holding the instanced models
            InstancedDictionaries.LoadDictionaries(_content, _graphics);
            InstancedDrawing.InitInstancing();

            // Discover dimensions of board. We only care about X/Z components
            Vector3 max = Vector3.Zero;
            Vector3 min = Vector3.Zero;
            for (int i = 0; i < _tiles.Length; i++)
            {
                if (_tiles[i].Position.X > max.X)
                    max.X = _tiles[i].Position.X + _tiles[i].BS.Radius;
                if (_tiles[i].Position.Z > max.Z)
                    max.Z = _tiles[i].Position.Z + _tiles[i].BS.Radius;
                if (_tiles[i].Position.X < min.X)
                    min.X = _tiles[i].Position.X - _tiles[i].BS.Radius;
                if (_tiles[i].Position.Z < min.Z)
                    min.Z = _tiles[i].Position.Z - _tiles[i].BS.Radius;
            }

            // Add tiles
            _quadtree = new Quadtree(new BoundingBox(min, max), _dim > 40 ? 2 : 0);
            for (int i = 0; i < _tiles.Length; i++)
            {
                _quadtree.AddBaseTile(_tiles[i]);

                if (_tiles[i].NaturalModifier != HexTileNaturalModifier.None)
                {
                    _quadtree.AddNaturalModifier(_tiles[i]);
                }
            }

            // restrict camera movement. scale up to allow camera out a little
            BoardVariables.boundingSphere = new BoundingSphere(_tiles[(_tiles.Length - 1) / 2].Position, ((_tiles[0].Position - _tiles[_tiles.Length - 1].Position).Length() / 2) * 1.0f);
        }
    }
}
